package come.demo.widgets.multiplicity;

import java.util.ArrayList;
import java.util.List;

public class HeaderFooterDataType<H, D, F> implements DataType {
    static final int SINGLE_DATA_TYPE_COUNT = 3;//一共三种，header,data,footer

    H headerData;
    final List<D> data = new ArrayList<>();
    F footerData;


    private int headerType;
    private int dataType;
    private int footerType;
    final int typeIndex;

    HeaderFooterDataType(int typeIndex, int headerType, int dataType, int footerType, H headerData, List<D> data, F footerData) {
        this.typeIndex = typeIndex;
        this.headerType = headerType;
        this.dataType = dataType;
        this.footerType = footerType;
        this.headerData = headerData;
        this.data.addAll(data);
        this.footerData = footerData;
    }

    public HeaderFooterDataType(int typeIndex, H headerData, List<D> data, F footerData) {
        this(typeIndex, 0, 0, 0, headerData, data, footerData);
    }


    @Override
    public int getItemCount() {
        int sz = data.size();
        if (hasHeader()) {
            ++sz;
        }
        if (hasFooter()) {
            ++sz;
        }
        return sz;
    }

    public boolean hasHeader() {
        return headerData != null;
    }

    public boolean hasFooter() {
        return footerData != null;
    }

    public boolean hasData() {
        return data.size() > 0;
    }


    public final int dataSize() {
        return data.size();
    }

    void reset(List<D> data) {
        this.data.clear();
        this.data.addAll(data);
    }

    @Override
    public int getItemType(int position) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;
        if (position < numHeaders) {
            return headerType;
        } else if (adjPosition < dataSize) {
            return dataType;
        } else if (adjPosition2 < numFooters) {
            return footerType;
        } else {
            throw new IllegalStateException("no data");
        }
    }

    @Override
    public Object getItem(int position) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;
        if (position < numHeaders) {
            return headerData;
        } else if (adjPosition < dataSize) {
            return data.get(adjPosition);
        } else if (adjPosition2 < numFooters) {
            return footerData;
        } else {
            throw new IllegalStateException("no data");
        }
    }

    @Override
    public boolean containsType(int type) {
        return type >= headerType && type <= footerType;
    }

    @Override
    public void generateTypes(int index) {
        headerType = index * SINGLE_DATA_TYPE_COUNT;
        dataType = headerType + 1;
        footerType = dataType + 1;
    }

    public int getTypeIndex() {
        return typeIndex;
    }

    public int getHeaderItemType() {
        return headerType;
    }

    public int getDataItemsType() {
        return dataType;
    }

    public int getFooterItemType() {
        return footerType;
    }

    @Override
    public boolean remove(int position) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;
        if (position < numHeaders) {
            headerData = null;
            return true;
        } else if (adjPosition < dataSize) {
            data.remove(adjPosition);
            return true;
        } else if (adjPosition2 < numFooters) {
//            data.remove(adjPosition2);
            footerData = null;
            return true;
        } else {
            throw new IllegalStateException("no data");
        }
    }

    @Override
    public boolean notifyItemChanged(int position, Object object) {
        return notifyItemRangeChanged(position, object);
    }


    @Override
    public boolean notifyItemRangeChanged(int position, Object... objects) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;
        if (position < numHeaders) {
            throwsHeaderException();
        } else if (adjPosition < dataSize) {
            for (int i = 0; i < objects.length; ++i) {
                data.set(adjPosition + i, (D) objects[i]);
            }
            return true;
        } else if (adjPosition2 < numFooters) {
            throwsFooterException();
        }


        return false;
    }

    @Override
    public boolean notifyItemInserted(int position, Object object) {
        return notifyItemRangeInserted(position, object);
    }

    @Override
    public boolean notifyItemRangeInserted(int position, Object... objects) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;
        if (position < numHeaders) {
            throwsHeaderException();
        } else if (adjPosition < dataSize) {
            for (int i = 0; i < objects.length; ++i) {
                data.add(adjPosition + i, (D) objects[i]);
            }
            return true;
        } else if (adjPosition2 < numFooters) {
            throwsFooterException();
        }


        return false;
    }

    @Override
    public boolean notifyItemMoved(int position, int toPosition) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;

        final int adjToPosition = toPosition - numHeaders;
        final int adjToPosition2 = adjToPosition - dataSize;
        if (position < numHeaders || toPosition < numHeaders) {
            throwsHeaderException();
        } else if (adjPosition < dataSize && adjToPosition < dataSize) {
            final Object f = data.get(adjPosition);
            final Object t = data.get(adjToPosition);
            data.set(adjPosition, (D) t);
            data.set(adjToPosition, (D) f);
            return true;
        } else if (adjPosition2 < numFooters || adjToPosition2 < numFooters) {
            throwsFooterException();
        }
        return false;
    }

    @Override
    public boolean notifyItemRemoved(int position) {

        return notifyItemRangeRemoved(position, 1);
    }

    @Override
    public boolean notifyItemRangeRemoved(int position, int itemCount) {
        final int numHeaders = hasHeader() ? 1 : 0;
        final int numFooters = hasFooter() ? 1 : 0;
        final int adjPosition = position - numHeaders;
        final int dataSize = dataSize();
        final int adjPosition2 = adjPosition - dataSize;
        if (position < numHeaders) {
            throwsHeaderException();
        } else if (adjPosition < dataSize) {
            for (int i = 0; i < itemCount; ++i) {
                data.remove(adjPosition);
            }
            return true;
        } else if (adjPosition2 < numFooters) {
            throwsFooterException();
        }

        return false;
    }

    public static void throwsHeaderException() {
        throw new IllegalStateException("can not update header");
    }

    public static void throwsFooterException() {
        throw new IllegalStateException("can not update footer");
    }

}
